import { ClientSideRowModelModule } from 'ag-grid-community';
import { RowGroupingModule } from 'ag-grid-enterprise';

import type { GridRowsOptions } from '../test-utils';
import { GridRows, TestGridsManager, applyTransactionChecked } from '../test-utils';

describe('group order maintenance', () => {
    const gridsManager = new TestGridsManager({
        modules: [ClientSideRowModelModule, RowGroupingModule],
    });

    beforeEach(() => gridsManager.reset());
    afterEach(() => gridsManager.reset());

    const gridRowsOptions: GridRowsOptions = {
        printIds: false,
        columns: ['athlete'],
    };

    test('new group is appended at end when groupMaintainOrder is true', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'I1' },
            { id: '2', country: 'Ireland', athlete: 'I2' },
            { id: '3', country: 'Italy', athlete: 'It1' },
        ];

        const api = gridsManager.createGrid('grid1', {
            columnDefs: [{ field: 'country', rowGroup: true, hide: true }, { field: 'athlete' }],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ ├── LEAF athlete:"I1"
            │ └── LEAF athlete:"I2"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"It1"
        `);

        // Add a new row that creates a new group (France)
        applyTransactionChecked(api, { add: [{ id: '4', country: 'France', athlete: 'F1' }] });

        // Expect the new group to be appended at the end (Ireland, Italy, France)
        await new GridRows(api, 'after add France', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ ├── LEAF athlete:"I1"
            │ └── LEAF athlete:"I2"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"It1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);
    });

    test('updating a row without changing group does not change group order (groupMaintainOrder=true)', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'I1' },
            { id: '2', country: 'Ireland', athlete: 'I2' },
            { id: '3', country: 'Italy', athlete: 'It1' },
            { id: '4', country: 'France', athlete: 'F1' },
        ];

        const api = gridsManager.createGrid('grid2', {
            columnDefs: [{ field: 'country', rowGroup: true, hide: true }, { field: 'athlete' }],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ ├── LEAF athlete:"I1"
            │ └── LEAF athlete:"I2"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"It1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);

        // Update a leaf inside existing group (Ireland), do not move group
        applyTransactionChecked(api, { update: [{ id: '2', country: 'Ireland', athlete: 'I2-upd' }] });

        // Group order should be unchanged
        await new GridRows(api, 'after update', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ ├── LEAF athlete:"I1"
            │ └── LEAF athlete:"I2-upd"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"It1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);
    });

    test('updating a row without changing group does not change group order (groupMaintainOrder=false)', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'I1' },
            { id: '2', country: 'Ireland', athlete: 'I2' },
            { id: '3', country: 'Italy', athlete: 'It1' },
            { id: '4', country: 'France', athlete: 'F1' },
        ];

        const api = gridsManager.createGrid('grid3', {
            columnDefs: [{ field: 'country', rowGroup: true, hide: true }, { field: 'athlete' }],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: false,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ ├── LEAF athlete:"I1"
            │ └── LEAF athlete:"I2"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"It1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);

        applyTransactionChecked(api, { update: [{ id: '2', country: 'Ireland', athlete: 'I2-upd' }] });

        // Group order should remain the same even when groupMaintainOrder is false
        await new GridRows(api, 'after update', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ ├── LEAF athlete:"I1"
            │ └── LEAF athlete:"I2-upd"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"It1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);
    });

    test('leaf-column sort preserves group order (groupMaintainOrder=true)', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'Zed' },
            { id: '2', country: 'Italy', athlete: 'Ann' },
            { id: '3', country: 'France', athlete: 'Mike' },
        ];

        const api = gridsManager.createGrid('grid4', {
            columnDefs: [
                { field: 'country', rowGroup: true, hide: true },
                { field: 'athlete', sortable: true },
            ],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', { printIds: false, columns: ['athlete'] }).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"Zed"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"Ann"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"Mike"
        `);

        // Sort by a leaf column. Group order should remain insertion order.
        api.applyColumnState({ state: [{ colId: 'athlete', sort: 'asc' }] });

        await new GridRows(api, 'leaf sort asc preserves group order', { printIds: false, columns: ['athlete'] })
            .check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"Zed"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"Ann"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"Mike"
        `);
    });

    test('group-column sort reorders groups (sorting coupled)', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'A' },
            { id: '2', country: 'Italy', athlete: 'B' },
            { id: '3', country: 'France', athlete: 'C' },
        ];

        const api = gridsManager.createGrid('grid5', {
            columnDefs: [
                { field: 'country', rowGroup: true, hide: true, sortable: true },
                { field: 'athlete', sortable: true },
            ],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', { printIds: false, columns: ['athlete'] }).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"A"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"B"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"C"
        `);

        // Sort by the primary grouped column; groups should reorder alphabetically: France, Ireland, Italy
        api.applyColumnState({ state: [{ colId: 'country', sort: 'asc' }] });

        await new GridRows(api, 'group sort asc', { printIds: false, columns: ['athlete'] }).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"C"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"A"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"B"
        `);

        // Change to desc: Italy, Ireland, France
        api.applyColumnState({ state: [{ colId: 'country', sort: 'desc' }] });

        await new GridRows(api, 'group sort desc', { printIds: false, columns: ['athlete'] }).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"B"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"A"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"C"
        `);
    });

    test('toggle from group sort to leaf sort preserves last group order', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'Z' },
            { id: '2', country: 'Italy', athlete: 'A' },
            { id: '3', country: 'France', athlete: 'M' },
        ];

        const api = gridsManager.createGrid('grid6', {
            columnDefs: [
                { field: 'country', rowGroup: true, hide: true, sortable: true },
                { field: 'athlete', sortable: true },
            ],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        // Force a group sort order first (desc): Italy, Ireland, France
        api.applyColumnState({ state: [{ colId: 'country', sort: 'desc' }] });
        await new GridRows(api, 'after group sort desc', { printIds: false, columns: ['athlete'] }).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"A"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"Z"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"M"
        `);

        // Now switch to a leaf sort; group order should remain the same
        api.applyColumnState({ state: [{ colId: 'athlete', sort: 'asc' }] });
        await new GridRows(api, 'leaf sort maintains last group order', { printIds: false, columns: ['athlete'] })
            .check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"A"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"Z"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"M"
        `);
    });
    test('after filtering removes a group, adding a new group appends at end', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'I1' },
            { id: '2', country: 'Italy', athlete: 'T1' },
            { id: '3', country: 'France', athlete: 'F1' },
        ];

        const api = gridsManager.createGrid('grid7', {
            columnDefs: [{ field: 'country', rowGroup: true, hide: true }, { field: 'athlete' }],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', gridRowsOptions).check(`
                ROOT
                ├─┬ LEAF_GROUP
                │ └── LEAF athlete:"I1"
                ├─┬ LEAF_GROUP
                │ └── LEAF athlete:"T1"
                └─┬ LEAF_GROUP
                · └── LEAF athlete:"F1"
            `);

        // Filter out Italy group entirely
        api.setGridOption('quickFilterText', 'I1'); // shows only Ireland
        await new GridRows(api, 'after filter Ireland only', gridRowsOptions).check(`
        ROOT
        └─┬ LEAF_GROUP
        · └── LEAF athlete:"I1"
        `);

        // Clear filter and add a new country; new group must append after prior order (Ire, Ita, Fra, then new Spain)
        api.setGridOption('quickFilterText', undefined);
        applyTransactionChecked(api, { add: [{ id: '4', country: 'Spain', athlete: 'S1' }] });

        await new GridRows(api, 'after add Spain', gridRowsOptions).check(`
                ROOT
                ├─┬ LEAF_GROUP
                │ └── LEAF athlete:"I1"
                ├─┬ LEAF_GROUP
                │ └── LEAF athlete:"T1"
                ├─┬ LEAF_GROUP
                │ └── LEAF athlete:"F1"
                └─┬ LEAF_GROUP
                · └── LEAF athlete:"S1"
            `);
    });

    test('after removing a group, adding a new group appends at end (sentinel append)', async () => {
        const rowData = [
            { id: '1', country: 'Ireland', athlete: 'I1' },
            { id: '2', country: 'Italy', athlete: 'It1' },
            { id: '3', country: 'France', athlete: 'F1' },
        ];

        const api = gridsManager.createGrid('grid8', {
            columnDefs: [{ field: 'country', rowGroup: true, hide: true }, { field: 'athlete' }],
            autoGroupColumnDef: { headerName: 'Country' },
            animateRows: false,
            groupDefaultExpanded: -1,
            groupMaintainOrder: true,
            rowData,
            getRowId: (p) => p.data.id,
        });

        await new GridRows(api, 'initial', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"I1"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"It1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);

        // Remove the middle group (Italy)
        applyTransactionChecked(api, { remove: [{ id: '2', country: 'Italy', athlete: 'It1' }] });

        await new GridRows(api, 'after remove Italy', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"I1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"F1"
        `);

        // Add a new group (Spain) - should append at end
        applyTransactionChecked(api, { add: [{ id: '4', country: 'Spain', athlete: 'S1' }] });

        await new GridRows(api, 'after add Spain', gridRowsOptions).check(`
            ROOT
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"I1"
            ├─┬ LEAF_GROUP
            │ └── LEAF athlete:"F1"
            └─┬ LEAF_GROUP
            · └── LEAF athlete:"S1"
        `);
    });
});
